package edu.neu.cs6515.player.strategy;

import java.util.List;
import java.util.Map.Entry;

import lombok.EqualsAndHashCode;

import com.google.java.contract.Ensures;
import com.google.java.contract.Requires;

import edu.neu.cs6515.board.BoardCoord;
import edu.neu.cs6515.board.HexagonBoard;
import edu.neu.cs6515.board.ScoreCalc;
import edu.neu.cs6515.util.Pair;
import edu.neu.cs6515.xml.IngeniousColor;
import edu.neu.cs6515.xml.Placement;
import edu.neu.cs6515.xml.Score;
import edu.neu.cs6515.xml.Tile;

/**
 * The strategy answers the question: What is the optimal placement in my
 * current situation?
 */
@EqualsAndHashCode



public abstract class AbstractStrategy {

    /**
     * Returns the next move recommended by the strategy based on the given
     * information
     * 
     * 
     * @param tilesInHand
     *            tiles that the player currently holds in his hands
     * @param numberOfPlayers
     *            total number of game participants
     * @param placementsSofar
     *            list of all placements that have been placed on the board so
     *            far
     * @param scoreBeforePlacement
     *            score of the player before the placement is executed
     * @return recommended Placement
     */
    @Requires({
            "tilesInHand != null" ,
            "tilesInHand.size() > 0", 
            "numberOfPlayers >= 2", 
            "numberOfPlayers <= 6", 
            "placementsSofar != null",
            "scoreBeforePlacement != null" })
    @Ensures("result != null")
    public abstract Placement getNextMove(List<Tile> tilesInHand,
            int numberOfPlayers, List<Placement> placementsSofar,
            Score scoreBeforePlacement);
    
    
    //TODO move this function to HexagonBoard
    /**
     * @param placementsSofar
     *            Tile placements that have been placed before
     * @param scoreTestBoard
     *            board to be used for the calculation of open spots
     * @return set that contains pairs of all open, legal placements on the
     *         board
     */
    protected List<Pair<BoardCoord>> getAllPossiblePlacements(
            final List<Placement> placementsSofar,
            final HexagonBoard scoreTestBoard) {
        for (Placement placement : placementsSofar) {
            scoreTestBoard.updateHexagonBoard(placement);
        }
        return scoreTestBoard.calculateLegalOpennings();
    }

    /**
     * @param pair
     *            pair of boardcoordinates
     * @param tile
     *            tile to be used for a placement
     * @return new placement based on the given pair of coordinates and the
     *         tile. First element of pair is combined with c0 of tile!
     */
    protected Placement createPlacement(final Pair<BoardCoord> pair,
            final Tile tile) {
        Placement attemptedPlacement = new Placement(tile.getC0(),
                tile.getC1(), pair.getFirst().getDistance(), pair.getFirst()
                        .getAngle(), pair.getSecond().getDistance(), pair
                        .getSecond().getAngle());
        return attemptedPlacement;
    }
    
    /**
     * determine if the placement cause the socreBeforePlacement get bonus
     * @param scoreBeforePlacement
     * @param hexBoard
     * @param placement
     * @return true if can get bonus
     */
    protected boolean isPlacementHitBonus(final Score scoreBeforePlacement, final HexagonBoard hexBoard, final Placement placement) {
        Score scoreAfter = hexBoard.scoreAMove(placement, scoreBeforePlacement);
        for (Entry<IngeniousColor, Integer> entry : scoreBeforePlacement.getIncreasingOrderSortedScore()) {
            if (entry.getValue() < ScoreCalc.BONUS_SCORE_LINE &&
                    scoreAfter.getPointsForColor(entry.getKey()) >= ScoreCalc.BONUS_SCORE_LINE) {
                return true;
            }
        }
        return false;
    }
}
